package practice_case;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

/*
*描述
请解析IP地址和对应的掩码，进行分类识别。要求按照A/B/C/D/E类地址归类，不合法的地址和掩码单独归类。

所有的IP地址划分为 A,B,C,D,E五类

A类地址从1.0.0.0到126.255.255.255;

B类地址从128.0.0.0到191.255.255.255;

C类地址从192.0.0.0到223.255.255.255;

D类地址从224.0.0.0到239.255.255.255；

E类地址从240.0.0.0到255.255.255.255


私网IP范围是：

从10.0.0.0到10.255.255.255

从172.16.0.0到172.31.255.255

从192.168.0.0到192.168.255.255


子网掩码为二进制下前面是连续的1，然后全是0。（例如：255.255.255.32就是一个非法的掩码）
（注意二进制下全是1或者全是0均为非法子网掩码）

注意：
1. 类似于【0.*.*.*】和【127.*.*.*】的IP地址不属于上述输入的任意一类，也不属于不合法ip地址，计数时请忽略
2. 私有IP地址和A,B,C,D,E类地址是不冲突的

输入描述：
多行字符串。每行一个IP地址和掩码，用~隔开。

请参考帖子https://www.nowcoder.com/discuss/276处理循环输入的问题。
输出描述：
统计A、B、C、D、E、错误IP地址或错误掩码、私有IP的个数，之间以空格隔开。

示例1
输入：
10.70.44.68~255.254.255.0
1.0.0.1~255.0.0.0
192.168.0.2~255.255.255.0
19..0.~255.255.255.0
复制
输出：
1 0 1 0 0 2 1
复制
说明：
10.70.44.68~255.254.255.0的子网掩码非法，19..0.~255.255.255.0的IP地址非法，所以错误IP地址或错误掩码的计数为2；
1.0.0.1~255.0.0.0是无误的A类地址；
192.168.0.2~255.255.255.0是无误的C类地址且是私有IP；
所以最终的结果为1 0 1 0 0 2 1
示例2
输入：
0.201.56.50~255.255.111.255
127.201.56.50~255.255.111.255
复制
输出：
0 0 0 0 0 0 0
复制
说明：
类似于【0.*.*.*】和【127.*.*.*】的IP地址不属于上述输入的任意一类，也不属于不合法ip地址，计数时请忽略
*
* */
public class HJ18识别有效的IP地址和掩码并进行分类统计 {
    private final static int[] constValues = {0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110/*, 0b11111111*/};

    // 只有数字和点
    // 刚好四组连续数字被点间断
    // 不能连点
    // 数字不能超过 255（数字不能有 0 前缀）
    public static int[] getFormatIPAddress(final char[] chars) {
        int addressFillingIndex = 0;
        int[] formatAddress = new int[4];
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '.') {
                // “.”号不能在开头或者结尾
                if (i == 0 || i == chars.length - 1) {
                    return null;
                }
                if (chars[i - 1] == '.') {
                    return null;
                }
            } else if (chars[i] >= '0' && chars[i] <= '9') {
                int startIndex = i;
                while (++i < chars.length && chars[i] >= '0' && chars[i] <= '9') ;
                // 填充最后一格时，遍历完数字，必然已经遍历完整个 chars，如果还有剩余字符，则不正确
                if (i < chars.length) {
                    if (addressFillingIndex == formatAddress.length - 1) {
                        return null;
                    }
                    // 数字之后必然为“.”
                    if (chars[i] != '.') {
                        return null;
                    }
                }
                //
                int len = i - startIndex;
                // 数字最多三位
                if (len > 3) {
                    return null;
                } else if (len > 1) {
                    if (chars[startIndex] == '0') {
                        return null;
                    }
                }
                // 字符串转 int
                int step = 1;
                for (int j = i - 1; j >= startIndex; j--) {
                    // 不允许 001 之类的格式
                    // !!!
//                    if (j != i - 1 && chars[j] == '0') {
//                        return null;
//                    }
                    formatAddress[addressFillingIndex] += (chars[j] - '0') * step;
                    step *= 10;
                }
                if (formatAddress[addressFillingIndex] > 255) {
                    return null;
                }
                addressFillingIndex++;
            } else {
                return null;
            }
        }
        // 未填充完毕，点分组数不够
        if (addressFillingIndex < formatAddress.length) {
            return null;
        }
        return formatAddress;
    }

    // 判断子网掩码正确性
    // 第一位最少也是 128，找到其中的非 255 的那一位，该位之前的必然是 255，之后必然是0，且该位只能为：
    // 0b10000000 0b11000000 0b11100000 0b11110000 0b11111000 0b11111100 0b11111110 0b11111111
    public static boolean isValidMaskAddress(final int[] formatMaskAddress) {
        if (formatMaskAddress[0] < 0b10000000) {
            return false;
        } else if (formatMaskAddress[0] == 0b11111111 && formatMaskAddress[1] == 0b11111111 && formatMaskAddress[2] == 0b11111111 && formatMaskAddress[3] == 0b11111111) {
            // 四个数都是 0b11111111，不行
            return false;
        } else {
            for (int i = 0; i < formatMaskAddress.length; i++) {
                // !!!
                if (formatMaskAddress[i] != 0b11111111) {
                    if (formatMaskAddress[i] != 0) {
                        // 不在限定内，不正确
                        if (Arrays.binarySearch(constValues, formatMaskAddress[i]) < 0) {
                            return false;
                        }
                    }
                    // 该数前面的应该都应该是 0b11111111
                    for (int j = 0; j < i; j++) {
                        if (formatMaskAddress[j] != 0b11111111) {
                            return false;
                        }
                    }
                    // 该数后面的数都应该是 0b00000000
                    for (int j = i + 1; j < formatMaskAddress.length; j++) {
                        if (formatMaskAddress[j] != 0) {
                            return false;
                        }
                    }
                    break;
                }
            }
        }
        return true;
    }

    public static void main(String[] args) throws IOException {
        // 使用 ~ 分隔 ip 地址和子网掩码
        // 都是有具体的 IP 地址的，没有通配符
        int classACount = 0;
        int classBCount = 0;
        int classCCount = 0;
        int classDCount = 0;
        int classECount = 0;
        int wrongIPOrMaskCount = 0;
        int privateIPCount = 0;

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            if("".equals(line)){
                break;
            }
            String[] items = line.split("~", 2);
            final String ipAddress = items[0];
            final String maskAddress = items[1];

            int[] formatIPAddress = getFormatIPAddress(ipAddress.toCharArray());
            int[] formatMaskAddress = getFormatIPAddress(maskAddress.toCharArray());

            // 错误IP或者掩码
            if (formatIPAddress == null || formatMaskAddress == null) {
                wrongIPOrMaskCount++;
                continue;
            }

            // 按题目，如果 IP 地址类似 【0.*.*.*】和【127.*.*.*】，则不判断子网掩码的正确性
            if (formatIPAddress[0] == 0 || formatIPAddress[0] == 127) {
                continue;
            }

            // 判断掩码正确性
            if (!isValidMaskAddress(formatMaskAddress)) {
                wrongIPOrMaskCount++;
                continue;
            }

            // 判断 IP 分类
            long v = formatIPAddress[0] * 16777216L + formatIPAddress[1] * 65536L + formatIPAddress[2] * 256L + formatIPAddress[3];
            if (v >= 0x01000000L && v <= 0x7EFFFFFFL) {
                classACount++;
            } else if (v >= 0x80000000L && v <= 0xBFFFFFFFL) {
                classBCount++;
            } else if (v >= 0xC0000000L && v <= 0xDFFFFFFFL) {
                classCCount++;
            } else if (v >= 0xE0000000L && v <= 0xEFFFFFFFL) {
                classDCount++;
            } else if (v >= 0xF0000000L && v <= 0xFFFFFFFFL) {
                classECount++;
            }

            if (v >= 0x0A000000L && v <= 0x0AFFFFFFL) {
                privateIPCount++;
            } else if (v >= 0xAC100000L && v <= 0xAC1FFFFFL) {
                privateIPCount++;
            } else if (v >= 0xC0A80000L && v <= 0xC0A8FFFFL) {
                privateIPCount++;
            }
        }

        System.out.println(classACount + " " + classBCount + " " + classCCount + " " + classDCount + " " + classECount + " " + wrongIPOrMaskCount + " " + privateIPCount);
    }
}
